package vote.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vote.config.VoteConfigProperties;
import vote.domain.AdminRequestUtil;
import vote.domain.PageParam;
import vote.domain.dto.VoteReq;
import vote.domain.entity.UserVote;
import vote.domain.entity.VoteContent;
import vote.domain.entity.VoteLog;
import vote.domain.vo.VoteContentResp;
import vote.mapper.UserVoteMapper;
import vote.mapper.VoteContentMapper;
import vote.mapper.VoteLogMapper;
import vote.service.VoteContentService;
import vote.service.VoteLogService;
import vote.service.VoteService;
import vote.util.BusinessException;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Description:
 * Author:han
 * Date: 2024-05-21
 */
@Service
public class VoteServiceImpl extends ServiceImpl<UserVoteMapper, UserVote> implements VoteService {
    @Autowired
    private VoteConfigProperties voteConfigProperties;
    @Resource
    private VoteContentService voteContentService;
    @Resource
    private VoteLogService voteLogService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean vote(VoteReq voteReq) {
        List<Long> voteItemIds = voteReq.getVoteItemIds();
        List<VoteContent> voteContents = voteContentService.listByIds(voteItemIds);
//        String userIp = AdminRequestUtil.getRequestUser().getIp();
//        Long currentUserId = AdminRequestUtil.getRequestUser().getUserId();
        String userIp = "12345";
        Long currentUserId = 2L;
        LocalDate today = LocalDate.now();
        // 活动有效期校验（假设VoteContent包含活动时间字段）
        checkActivityExpired(voteContents);
        // IP 日投票次数校验
        checkIpVoteLimit(userIp, voteItemIds.size());
        //  校验投票项目存在性
        checkVoteItemsExist(voteItemIds);
        //之前投票记录
        List<UserVote> userCurrentVotes = getUserVotesWithLock(currentUserId, today);
        // 大类投票限制校验
        checkCategoryLimits(userCurrentVotes, voteContents);
        //是否有重复投票的检查
        Set<Long> existedItemIds = userCurrentVotes.stream()
                .map(UserVote::getItemId)
                .collect(Collectors.toSet());
        List<Long> duplicateItemIds = voteItemIds.stream()
                .filter(existedItemIds::contains)
                .collect(Collectors.toList());
        if (!duplicateItemIds.isEmpty()) {
            throw new BusinessException("不能重复投票");
        }
        //初始化
        initLog(voteConfigProperties, currentUserId, userIp, today);
        // 4. 总投票次数校验
        checkTotalVoteLimit(userCurrentVotes, voteItemIds.size());
        lock(voteItemIds.size(), currentUserId, today);
        // 6. 保存投票记录
        saveVoteRecords(currentUserId, userIp, today, voteContents);
        return true;
    }

    private void lock(int voteItemSize, Long userId, LocalDate voteDate) {
        int totalMaxVotes = voteConfigProperties.getTotalMaxVotes();
        LambdaUpdateWrapper<VoteLog> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .eq(VoteLog::getUserId, userId)
                .eq(VoteLog::getVoteDate, voteDate)
                .le(VoteLog::getCount, totalMaxVotes - voteItemSize)
                .setSql("count = count + " + voteItemSize);
        boolean update = voteLogService.update(null, updateWrapper);
        if (!update) {
            throw new RuntimeException("投票失败，超出投票限制");
        }
    }

    private void initLog(VoteConfigProperties voteConfigProperties, Long currentUserId, String userIp, LocalDate today) {
        VoteLog voteLog = new VoteLog();
        voteLog.setUserId(currentUserId);
        voteLog.setIp(userIp);
        voteLog.setVoteDate(today);
        voteLog.setCount(0);
        voteLog.setLimitCount(voteConfigProperties.getDailyLimit());
        voteLogService.save(voteLog);
    }

    private void checkIpVoteLimit(String userIp, int voteCount) {
        int dailyIpLimit = voteConfigProperties.getTotalMaxVotes();
        long count = this.count(new LambdaQueryWrapper<UserVote>()
                .eq(UserVote::getUserIp, userIp));
        if (count + voteCount > dailyIpLimit) {
            throw new BusinessException("网络异常");
        }
    }

    private void checkVoteItemsExist(List<Long> voteItemIds) {
        List<VoteContent> voteContents = voteContentService.list(
                new LambdaQueryWrapper<VoteContent>().in(VoteContent::getId, voteItemIds)
        );
        if (voteContents.size() != voteItemIds.size()) {
            throw new BusinessException("存在无效的投票项目 ID");
        }
    }

    private List<UserVote> getUserVotesWithLock(Long userId, LocalDate voteDate) {
        return this.list(
                new LambdaQueryWrapper<UserVote>()
                        .eq(UserVote::getUserId, userId)
                        .eq(UserVote::getVoteDate, voteDate)
        );
    }

    private void checkTotalVoteLimit(List<UserVote> userVotes, int voteItemCount) {
        int totalMaxVotes = voteConfigProperties.getDailyLimit();
        if (userVotes.size() + voteItemCount > totalMaxVotes) {
            throw new BusinessException("您今天投票次数已经用尽了");
        }
    }

    private void checkActivityExpired(List<VoteContent> voteContents) {
        if (voteContents.stream().anyMatch(vc -> vc.getEndAt().isBefore(LocalDate.now()))) {
            throw new BusinessException("投票活动已过期");
        }
    }

    private void checkCategoryLimits(List<UserVote> userVotes, List<VoteContent> voteContents) {
        Map<String, List<UserVote>> areaVoteMap = userVotes.stream().collect(Collectors.groupingBy(UserVote::getArea));
        long newAreaCount = voteContents.stream().map(VoteContent::getArea).distinct().count();
        int maxCategories = voteConfigProperties.getMaxCategories();
        if (areaVoteMap.size() + newAreaCount > maxCategories) {
            throw new BusinessException("大类不能超过 " + maxCategories + " 个");
        }
        int itemsPerCategory = voteConfigProperties.getItemsPerCategory();
        for (VoteContent voteContent : voteContents) {
            String area = voteContent.getArea();
            long areaVoteCount = areaVoteMap.getOrDefault(area, Collections.emptyList()).size() + 1;
            if (areaVoteCount > itemsPerCategory) {
                throw new BusinessException("每个大类最多投 " + itemsPerCategory + " 票");
            }
        }
    }

    private void saveVoteRecords(Long userId, String userIp, LocalDate voteDate, List<VoteContent> voteContents) {
        List<UserVote> userVoteSaveList = new ArrayList<>();
        for (VoteContent voteContent : voteContents) {
            UserVote userVote = new UserVote();
            userVote.setUserId(userId);
            userVote.setItemId(voteContent.getId());
            userVote.setVoteDate(voteDate);
            userVote.setArea(voteContent.getArea());
            userVote.setUserIp(userIp);
            userVoteSaveList.add(userVote);
        }
        this.saveBatch(userVoteSaveList);
    }
}
